/*
 * Copyright (c) 2009-2010 HIT Microelectronic Center
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Gou Pengfei
 *
 * Date:    Feb. 2011
 *
 */

#include "base/intmath.hh"
#include "cpu/edge/pred/piecewise_exit.hh"
#include "cpu/base.hh"

template<class Impl>
PiecewiseExitP<Impl>::PiecewiseExitP(DerivEdgeCPUParams *params)
    :
    // History bits
    msbHistoryBits(params->pwMsbHistoryBits),
    mbHistoryBits(params->pwMbHistoryBits),
    lsbHistoryBits(params->pwLsbHistoryBits),
    // Path table size
    msbPathTableSize(params->pwMsbPathTableSize),
    mbPathTableSize(params->pwMbPathTableSize),
    lsbPathTableSize(params->pwLsbPathTableSize),
    // Table size
    msbTableSize(params->pwMsbTableSize),
    mbTableSize(params->pwMbTableSize),
    lsbTableSize(params->pwLsbTableSize),
    // Local history bits
    msbLocalHistoryBits(params->pwMsbLocalHistoryBits),
    mbLocalHistoryBits(params->pwMbLocalHistoryBits),
    lsbLocalHistoryBits(params->pwLsbLocalHistoryBits),
    // Local history table size
    msbLocalHistoryTableSize(params->pwMsbLocalHistoryTableSize),
    mbLocalHistoryTableSize(params->pwMbLocalHistoryTableSize),
    lsbLocalHistoryTableSize(params->pwLsbLocalHistoryTableSize),
    // Others
    weightBits(params->weightBits),
    blockShiftAmt(params->blockShiftAmt)
{
    msbPredictor = new PiecewiseBP<Impl>(msbHistoryBits,
                                         msbLocalHistoryBits,
                                         msbPathTableSize,
                                         msbTableSize,
                                         msbLocalHistoryTableSize,
                                         weightBits,
                                         blockShiftAmt);

    mbPredictor = new PiecewiseBP<Impl>(mbHistoryBits,
                                        mbLocalHistoryBits,
                                        mbPathTableSize,
                                        mbTableSize,
                                        mbLocalHistoryTableSize,
                                        weightBits,
                                        blockShiftAmt);

    lsbPredictor = new PiecewiseBP<Impl>(lsbHistoryBits,
                                         lsbLocalHistoryBits,
                                         lsbPathTableSize,
                                         lsbTableSize,
                                         lsbLocalHistoryTableSize,
                                         weightBits,
                                         blockShiftAmt);

    // Initialize each predictor to proper mode
    if (params->exitPiecewiseHistMode == "ExitHistory") {
        msbPredictor->initMode(PiecewiseBP<Impl>::ExitHistory);
        mbPredictor->initMode(PiecewiseBP<Impl>::ExitHistory);
        lsbPredictor->initMode(PiecewiseBP<Impl>::ExitHistory);

        historyMode = ExitHistory;
    } else if (params->exitPiecewiseHistMode == "SingleBitHistory") {
        msbPredictor->initMode(PiecewiseBP<Impl>::SingleBitHistory);
        mbPredictor->initMode(PiecewiseBP<Impl>::SingleBitHistory);
        lsbPredictor->initMode(PiecewiseBP<Impl>::SingleBitHistory);

        historyMode = SingleBitHistory;
    } else {
        fatal("Invalid history mode for piecewise exit predictor.\n");
    }
} 

template<class Impl>
PiecewiseExitP<Impl>::~PiecewiseExitP()
{
    DPRINTF(EdgePredUnit,"Perceptron destructed!\n");

    delete msbPredictor;
    delete mbPredictor;
    delete lsbPredictor;
}

template<class Impl>
void
PiecewiseExitP<Impl>::reset()
{
    msbPredictor->reset();
    mbPredictor->reset();
    lsbPredictor->reset();
}

template<class Impl>
TheISA::ExitID
PiecewiseExitP<Impl>::lookup(BlockID block_id, BlockID oldest_block_id,
        Addr &block_pc, ThreadID tid)
{
    bool msb = msbPredictor->lookup(block_id, oldest_block_id, block_pc, tid);
    bool mb = mbPredictor->lookup(block_id, oldest_block_id, block_pc, tid);
    bool lsb = lsbPredictor->lookup(block_id, oldest_block_id, block_pc, tid);

    ExitID exit_id = exitGen(msb, mb, lsb);

    // Speculatively update the history
    if (historyMode == ExitHistory) {
        msbPredictor->speculativeUpdate(block_pc, block_id, exit_id, tid);
        mbPredictor->speculativeUpdate(block_pc, block_id, exit_id, tid);
        lsbPredictor->speculativeUpdate(block_pc, block_id, exit_id, tid);
    }
    
    return exit_id;
}

template<class Impl>
void
PiecewiseExitP<Impl>::update(BlockID block_id, Addr &block_pc,
        ExitID actual_exit_id, ExitID pred_exit_id, ThreadID tid)
{
    bool msb = (((actual_exit_id >> 2) & 0x1) == 1) ? true : false;
    bool mb = (((actual_exit_id >> 1) & 0x1) == 1) ? true : false;
    bool lsb = ((actual_exit_id & 0x1) == 1) ? true : false;

    bool msb_mispredicted = (((actual_exit_id >> 2) & 0x1) !=
            ((pred_exit_id >> 2) & 0x1)) ? true : false;

    bool mb_mispredicted = (((actual_exit_id >> 1) & 0x1) !=
            ((pred_exit_id >> 1) & 0x1)) ? true : false;

    bool lsb_mispredicted = ((actual_exit_id & 0x1) !=
            (pred_exit_id & 0x1)) ? true : false;

    msbPredictor->update(block_id, block_pc, tid, msb, msb_mispredicted);
    mbPredictor->update(block_id, block_pc, tid, mb, mb_mispredicted);
    lsbPredictor->update(block_id, block_pc, tid, lsb, lsb_mispredicted);

    // Update the local history non-speculatively
    if (historyMode == ExitHistory) {
        msbPredictor->updateLocalHistoryTable(block_id, block_pc, actual_exit_id,
                                              tid);
        mbPredictor->updateLocalHistoryTable(block_id, block_pc, actual_exit_id,
                                              tid);
        lsbPredictor->updateLocalHistoryTable(block_id, block_pc, actual_exit_id,
                                              tid);
    }
}

template<class Impl>
void
PiecewiseExitP<Impl>::squash(BlockID block_id, ThreadID tid)
{
    msbPredictor->squash(block_id, tid);
    mbPredictor->squash(block_id, tid);
    lsbPredictor->squash(block_id, tid);
}

template<class Impl>
void
PiecewiseExitP<Impl>::squash(Addr block_pc, BlockID block_id,
        ExitID actual_exit_id, ExitID pred_exit_id, ThreadID tid)
{
    bool msb = (((actual_exit_id >> 2) & 0x1) == 1) ? true : false;
    bool mb = (((actual_exit_id >> 1) & 0x1) == 1) ? true : false;
    bool lsb = ((actual_exit_id & 0x1) == 1) ? true : false;

    bool msb_mispredicted = (((actual_exit_id >> 2) & 0x1) !=
            ((pred_exit_id >> 2) & 0x1)) ? true : false;

    bool mb_mispredicted = (((actual_exit_id >> 1) & 0x1) !=
            ((pred_exit_id >> 1) & 0x1)) ? true : false;

    bool lsb_mispredicted = ((actual_exit_id & 0x1) !=
            (pred_exit_id & 0x1)) ? true : false;

    DPRINTF(EdgePredUnit, "Piecewise exit predictor:"
            " actual exit id:%i, pred exit id:%i"
            " msb_mispredict:%i, mb_mispredict:%i, lsb_mispredict:%i\n",
            actual_exit_id, pred_exit_id,
            msb_mispredicted, mb_mispredicted, lsb_mispredicted);

    assert(msb_mispredicted || mb_mispredicted || lsb_mispredicted);

    if (msb_mispredicted) { 
        if (historyMode == ExitHistory) {
            msbPredictor->squash(block_pc, block_id, actual_exit_id, tid);
        } else if (historyMode == SingleBitHistory) {
            msbPredictor->squash(block_pc, block_id, msb, tid);
        } else {
            panic("Invalid history mode for piecewise exit predictor.\n");
        }
    }

    if (mb_mispredicted) {
        if (historyMode == ExitHistory) {
            mbPredictor->squash(block_pc, block_id, actual_exit_id, tid);
        } else if (historyMode == SingleBitHistory) {
            mbPredictor->squash(block_pc, block_id, mb, tid);
        } else {
            panic("Invalid history mode for piecewise exit predictor.\n");
        }
    }

    if (lsb_mispredicted) {
        if (historyMode == ExitHistory) {
            lsbPredictor->squash(block_pc, block_id, actual_exit_id, tid);
        } else if (historyMode == SingleBitHistory) {
            lsbPredictor->squash(block_pc, block_id, lsb, tid);
        } else {
            panic("Invalid history mode for piecewise exit predictor.\n");
        }
    }
}

template<class Impl>
TheISA::ExitID
PiecewiseExitP<Impl>::exitGen(bool msb, bool mb, bool lsb)
{
    ExitID exit_id = 0;
    if (lsb) exit_id = exit_id | 0x1;
    if (mb) exit_id = exit_id | 0x2;
    if (msb) exit_id = exit_id | 0x4;
    return exit_id;
}

